package com.jt.auth.config;

import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * Oauth2是一种协议或规范,它定义了完成用户身份认证和授权的一个方式,
 * 例如:基于用户名和密码进行认证,基于第三方令牌进行认证(例如qq,微信登录)等
 * 在这个认证授权过程中可能需要有如下几个部分:
 * 1)你要保护的资源(数据):认证授权的目的不就是为了保护数据的安全吗
 * 2)资源管理服务器(资源对应的服务器)
 * 3)认证管理服务器(负责对用户身份进行认证)
 * 4)认证客户端对象(例如html表单,三方令牌,....)
 * 5)需要认证的用户(....)

 * 思考:既然是认证,那如何对用户进行认证?
 * 1)提供认证的地址(去哪里认证?例如去派出所的户籍科办理身份证,去民政局的结婚登记处办理结婚登记.)
 * 2)用户需要携带什么信息去认证?(去民政局登记结婚需要带着户口本,身份证等)
 * 3)负责为用户完成认证的对象是谁?(民政局谁帮你办理结婚登记)
 * 如上这些规范都由谁进行设计和定义? Oauth2
 */
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {
    //@Autowired
    private BCryptPasswordEncoder passwordEncoder;
    //@Autowired
    private AuthenticationManager authenticationManager;
    //@Autowired
    private TokenStore tokenStore;
    //@Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    /**
     * 定义公开认证地址(用户需要携带资料到这个你指定的地址进行认证)
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //http://ip:port/oauth/token?username=.....&....
        security//公开认证地址(/oauth/token),用户要去认证的地址
                 .tokenKeyAccess("permitAll()")
                 //公开校验token的地址(/oauth/check_token)
                 .checkTokenAccess("permitAll()")
                 //允许基于form表单的认证方式
                 .allowFormAuthenticationForClients();
    }

    /**
     * 用户需要携带什么信息到认证服务器进行认证.
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
          clients.inMemory()
                  //定义客户端标识(客户端到认证服务地址去认证时需要携带这个信息)
                  .withClient("gateway-client")
                  //定义客户端携带的密钥
                  .secret(passwordEncoder.encode("123456"))
                  //定义认证类型(我们本次采用基于密码方式,刷新令牌方式的认证)
                  .authorizedGrantTypes("password","refresh_token")
                  //作用域(满足如上条件的所有客户端都可以进行认证)
                  .scopes("all");
    }
    /**
     * 定义帮你完整认证的对象是,认证成功以后颁发什么令牌?
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints
                //定义认证对象
                .authenticationManager(authenticationManager)
                //定义令牌服务(有默认令牌,但默认令牌不能满足我们需求)
                .tokenServices(tokenServices());
        //.....
    }

    @Bean
    public AuthorizationServerTokenServices tokenServices(){
        //1.构建令牌服务对象
        DefaultTokenServices tokenServices=new DefaultTokenServices();
        //2.设置令牌创建及存储
        tokenServices.setTokenStore(tokenStore);
        //3.设置令牌增强对象(默认是uuid格式的令牌)
        tokenServices.setTokenEnhancer(jwtAccessTokenConverter);
        //4.设置刷新令牌(服务端要创建一个刷新令牌)
        tokenServices.setSupportRefreshToken(true);
        //5.设置访问令牌,刷新令牌的有效期
        tokenServices.setAccessTokenValiditySeconds(3600);
        tokenServices.setRefreshTokenValiditySeconds(5400);
        return tokenServices;
    }


}
//http-client
//POST http://localhost:8071/oauth/token?username=admin&password=123456
//&client_id=gateway-client&client_secret=123456&grant_type=password

